package nl.boelen.game;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import nl.boelen.game.graphics.CustomImage;
import nl.boelen.game.graphics.Screen;
import nl.boelen.game.graphics.SpriteSheet;
import nl.boelen.game.handler.MouseHandler;
import nl.boelen.game.handler.keyboardHandler;
import nl.boelen.game.level.Level;

public class Game extends Canvas implements Runnable {
    // private static GraphicsDevice gd =
    // GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
    // private static final int WIDTH = gd.getDisplayMode().getWidth() - 200;
    // private static final int HEIGHT = gd.getDisplayMode().getHeight() - 200;
    public static final int TILE_SIZE = 16;

    // public static int zoom = 300;
    private static final int WIDTH = 800;
    private static final int HEIGHT = (WIDTH / 4) * 3;
    public static final double SCALE = 1;

    public static final Dimension dimension = new Dimension((int) (WIDTH * SCALE), (int) (HEIGHT * SCALE));
    public static final String GAMENAME = "Hospital Builder " + WIDTH + "x" + HEIGHT;
    public static final double FRAMES_PER_SECOND = 60;

    private boolean running = false;
    private int tickCount = 0;
    private int ticksLastSecond = 0;
    private int framesLastSecond = 0;
    private EGameState gameState;
    private Thread runningThread;
    private JFrame frame;
    private Screen screen;
    private Level level;
    private keyboardHandler keyboardHandler;
    private MouseHandler mouseHandler;

    /* The image displayed on the screen */
    private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
    /* Represent all the pixels in the image */
    private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

    private BufferedImage img = new CustomImage("/sprite_sheet.png").getImage();

    public Game() {
        /* Set sizes of the frame so everything is correct */
        setMinimumSize(dimension);
        setMaximumSize(dimension);
        setPreferredSize(dimension);

        frame = new JFrame(GAMENAME);
        frame.setUndecorated(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());

        /* Center the Canvas in the frame */
        frame.add(this, BorderLayout.CENTER);
        frame.setResizable(false);
        frame.pack();
        /* Centers it on the screen */
        // frame.setLocationRelativeTo(null);
        frame.setLocation(10, 10);
        frame.setVisible(true);
    }

    public void init() {
        // LOG.setUseParentHandlers(false);
        screen = new Screen(WIDTH, HEIGHT, new SpriteSheet("/sprite_sheet.png"));
        keyboardHandler = new keyboardHandler(this);
        mouseHandler = new MouseHandler(this, screen);
        level = new Level(mouseHandler, screen);
        gameState = EGameState.GAME;

    }

    public synchronized void start() {
        running = true;
        runningThread = new Thread(this);
        runningThread.start();
    }

    public synchronized void pause() {
        running = false;
    }

    public synchronized void resume() {
        running = true;
    }

    public synchronized void quit() {
        running = false;
        WindowEvent wev = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev);
    }

    public void run() {
        init();

        /* Limit the rendering */
        long lastTime = System.nanoTime();
        double nsPerTick = 1000000000D / FRAMES_PER_SECOND;

        int ticks = 0;
        int frames = 0;

        long lastTimer = System.currentTimeMillis();
        double processedNs = 0;
        boolean shouldRender;

        for (;;) {
            long now = System.nanoTime();
            processedNs += (now - lastTime) / nsPerTick;
            lastTime = now;

            // XXX set to false to stop overloading the system
            shouldRender = true;
            while (processedNs >= 1) {
                ticks++;
                tick();
                processedNs -= 1;
                shouldRender = true;
            }

            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (shouldRender) {
                frames++;
                render();
            }

            if (System.currentTimeMillis() - lastTimer >= 1000) {
                lastTimer += 1000;
                framesLastSecond = frames;
                frames = 0;
                ticksLastSecond = ticks;
                ticks = 0;
            }
        }
    }

    public void tick() {
        tickCount++;

        if (keyboardHandler.quit.isPressed()) {
            quit();
        }

        switch (gameState) {
            case MENU :
                menuTick();
                break;
            case GAME :
                gameTick();
                break;
            default :
                break;
        }
    }

    private void menuTick() {

    }

    private void gameTick() {
        if (running) {
            if (keyboardHandler.pause.isPaused()) {
                pause();
            }
            if (keyboardHandler.up.isPressed()) {
                screen.addOffset(0, 1);
            }
            if (keyboardHandler.down.isPressed()) {
                screen.addOffset(0, -1);
            }
            if (keyboardHandler.left.isPressed()) {
                screen.addOffset(1, 0);
            }
            if (keyboardHandler.right.isPressed()) {
                screen.addOffset(-1, 0);
            }
            mouseHandler.tick();

            level.tick();
        } else {
            if (!keyboardHandler.pause.isPaused()) {
                resume();
            }
        }
    }

    public void render() {
        /* Object to organize the data on the canvas */
        BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            /*
             * The higher the number the better the reduces cross images while
             * updating The higher the more processing power you need
             */
            createBufferStrategy(3);
            return;
        }
        Graphics g = bs.getDrawGraphics();
        g.drawRect(0, 0, getWidth(), getHeight());

        switch (gameState) {
            case MENU :
                menuRender(g);
                break;
            case GAME :
                gameRender(g);
                break;
            default :
                break;
        }

        // if (img != null) {
        // int x = (getWidth() / 4);
        // int y = (getHeight() / 8);
        // g.drawImage(img, x, y, zoom, zoom, this);
        // }

        /* Free the memory used because we are not using it anymore */
        g.dispose();
        bs.show();
    }

    private void menuRender(Graphics g) {

    }

    private void gameRender(Graphics g) {
        if (running) {
            level.render();
        }
        renderPixels(g);

        g.setColor(Color.WHITE);
        g.drawString("FPS:" + framesLastSecond, 10, 20);
    }

    public void setGameState(EGameState gameState) {
        this.gameState = gameState;
    }

    private void renderPixels(Graphics g) {
        for (int y = 0; y < screen.height; y++) {
            for (int x = 0; x < screen.width; x++) {
                pixels[x + y * WIDTH] = screen.pixels[x + y * screen.width];
            }
        }
        g.fillRect(0, 0, screen.width, screen.height);
        g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
    }

    public static void main(String[] arg) {
        new Game().start();
    }

    public Screen getScreen() {
        return screen;
    }
}
